
#include <string.h>
#include <jni.h>

#include <errno.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <asm/page.h>
#include <unistd.h>
#include <android/log.h>
#define  D(x...)  __android_log_print(ANDROID_LOG_INFO,"getFrameBuffer",x)

struct fb_var_screeninfo g_vinfo = {0,};

static void init_vinfo(void)
{
	if (0 != g_vinfo.bits_per_pixel) {
		return;
	}

	int fb = open("/dev/graphics/fb0", O_RDONLY);
	if (fb >= 0) {
		if (ioctl(fb, FBIOGET_VSCREENINFO, &g_vinfo) == 0) {
		}
		close(fb);
	}
	else {
		D("error : fb(%d)\n", fb);
	}
}

/** 프레임버퍼 너비를 구한다.
 * @param env
 * @param thiz
 */
JNIEXPORT jint JNICALL Java_com_qd_extcam_ExtCamActivity_getWidth(JNIEnv *env, jobject thiz)
{
	init_vinfo();
	return g_vinfo.xres;
}

/** 프레임버퍼 높이를 구한다.
 * @param env
 * @param thiz
 */
JNIEXPORT jint JNICALL Java_com_qd_extcam_ExtCamActivity_getHeight(JNIEnv *env, jobject thiz)
{
	init_vinfo();
	return g_vinfo.yres;
}

/** 프레임버퍼 픽셀 크기를 구한다.
 * @param env
 * @param thiz
 */
JNIEXPORT jint JNICALL Java_com_qd_extcam_ExtCamActivity_getBitsPerPixel(JNIEnv *env, jobject thiz)
{
	init_vinfo();
	return g_vinfo.bits_per_pixel;
}

/** 프레임버퍼 이미지를 구한다.
 * @param env
 * @param thiz
 */
JNIEXPORT jint JNICALL Java_com_qd_extcam_ExtCamActivity_getFrameBuffer(JNIEnv *env, jobject thiz, jbyteArray jByte)
{
	void const* mapbase = MAP_FAILED;
	ssize_t mapsize = -1;
	void const* base = 0;
	uint32_t w, h, f;
	size_t size = 0;

	D("start++++++++++++++++++++++++\n");
	int fb = open("/dev/graphics/fb0", O_RDONLY);
	if (fb >= 0) {
		struct fb_var_screeninfo vinfo;
		if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) {
			uint32_t bytespp = 0;
			switch (vinfo.bits_per_pixel) {
			case 16:	//PIXEL_FORMAT_RGB_565;
				bytespp = 2;
				break;
			case 24:	//PIXEL_FORMAT_RGB_888;
				bytespp = 3;
				break;
			case 32:	//PIXEL_FORMAT_RGBX_8888;
				// TODO: do better decoding of vinfo here
				bytespp = 4;
				break;
			}
			D("bytespp = %d\n", bytespp);

			if (bytespp != 0) {
				size_t offset = (vinfo.xoffset + (vinfo.yoffset * vinfo.xres)) * bytespp;
				w = vinfo.xres;
				h = vinfo.yres;
				size = w * h * bytespp;
				mapsize = offset + size;
				mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
				if (mapbase != MAP_FAILED) {
					base = (void const *)((char const *)mapbase + offset);
				}
			}
		}
		close(fb);
	}
	else {
		D("error : fb(%d)\n", fb);
	}

	if (base) {
		// set buffer
		(*env)->SetByteArrayRegion(env, jByte, 0, size, (jbyte*)base);
	}

	if (mapbase != MAP_FAILED) {
		munmap((void *)mapbase, mapsize);
	}

	return 0;
}
